//
//  LJServer.m
//  LJClientKit
//
//  Created by che on 12/7/09.
//  Copyright 2009 AntonChebotaev. All rights reserved.
//

#import "LJServer.h"
#import "LJTypograph.h"


// Exceptions
NSString * const LJServerBadInitException = @"LJServerBadInitException";

// Tech constants
NSString * const LJServerDefaultClientVersion = @"MacOSX-LJClientKit/0.0.1";

// Notifications
NSString * const LJServerResponceErrorNotification = @"LJServerResponceErrorNotification";
NSString * const LJServerLivejournalErrorNotification = @"LJServerLivejournalErrorNotification";


@implementation LJServer

@synthesize serverURL;
@synthesize clientVersion;

- (id)init {
	[self dealloc];
    @throw [NSException exceptionWithName:LJServerBadInitException
								   reason:@"Initialize LJServer with simple init without URL and"
								 userInfo:nil];
    return nil;	
}

- (id)initWithURL:(NSURL *)anURL {
	self = [super init];
	if (self) {
		serverURL = [anURL retain];
		clientVersion = [LJServerDefaultClientVersion retain];
    }
    return self;	
}

- (void) dealloc {
	[serverURL release];
	[clientVersion release];
	[super dealloc];
}

- (id)initWithCoder:(NSCoder *)decoder {
	self = [super init];
    if (self) {
        serverURL = [[decoder decodeObjectForKey:@"LJServerCodingServerURL"] retain];
		clientVersion = [[decoder decodeObjectForKey:@"LJServerCodingClientVersion"] retain];
        NSAssert(serverURL != nil, @"LJServer decoded without an URL.");
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:serverURL forKey:@"LJServerCodingServerURL"];
    [encoder encodeObject:clientVersion forKey:@"LJServerCodingClientVersion"];
}

- (BOOL)isEqual:(id)anObject {
	return [serverURL isEqual:[anObject serverURL]];
}

- (NSString *)description {
	return [serverURL absoluteString];
}

- (LJResponseDictionary *)loginForUsername:(NSString *)anUsername password:(NSString *)aPassword {
	XMLRPCRequest *request = [[XMLRPCRequest alloc] initWithURL: serverURL];
	
	NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:
								anUsername,	@"username",
								aPassword, 	@"password",
								@"clear", 	@"auth_method",
								@"1",		@"ver",
								@"1", 		@"getpickws",
								@"1", 		@"getpickwurls",
								nil];
	
	[request setMethod: @"LJ.XMLRPC.login" withParameter: parameters];
	
	XMLRPCResponse *response = [XMLRPCConnection sendSynchronousXMLRPCRequest:request];	
    [request release];
	
	if (!response) {
		[[NSNotificationCenter defaultCenter] postNotificationName:LJServerResponceErrorNotification object:self];
		NSLog(@"ERROR: (loginForUsername:password:) Failed to get responce from server");
		return nil;
	}	
	if ([response isFault]) {
		NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
							  [[response object] objectForKey:@"faultCode"], @"LJServerErrorCode",
							  [[response object] objectForKey:@"faultString"], @"LJServerErrorString",
							  nil];
		[[NSNotificationCenter defaultCenter] postNotificationName:LJServerLivejournalErrorNotification object:self userInfo:info];
		NSLog(@"ERROR: (loginForUsername:password:) Server returned failure. Code: %@, description: %@", 
			  [[response object] objectForKey:@"faultCode"], 
			  [[response object] objectForKey:@"faultString"]);
		return nil;
	} else {
		return [response object];
	}
}

- (LJResponseDictionary *)friendsForUsername:(NSString *)anUsername password:(NSString *)aPassword {
	XMLRPCRequest *request = [[XMLRPCRequest alloc] initWithURL: serverURL];
	
	NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:
								anUsername,	@"username",
								aPassword, 	@"password",
								@"clear", 	@"auth_method",
								@"1",		@"ver",
//								@"1",		@"includefriendof", // Uncomment to include who friended user
//								@"1", 		@"includegroups",   // Uncomment to include friend groups
//								@"1", 		@"includebdays",    // Uncomment to include birthdays
								nil];
	
	[request setMethod: @"LJ.XMLRPC.getfriends" withParameter: parameters];
	
	XMLRPCResponse *response = [XMLRPCConnection sendSynchronousXMLRPCRequest:request];	
    [request release];
	
	if (!response) {
		[[NSNotificationCenter defaultCenter] postNotificationName:LJServerResponceErrorNotification object:self];
		NSLog(@"ERROR: (friendsForUsername:password:) Failed to get responce from server");
		return nil;
	}	
	if ([response isFault]) {
		NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
							  [[response object] objectForKey:@"faultCode"], @"LJServerErrorCode",
							  [[response object] objectForKey:@"faultString"], @"LJServerErrorString",
							  nil];
		[[NSNotificationCenter defaultCenter] postNotificationName:LJServerLivejournalErrorNotification object:self userInfo:info];
		NSLog(@"ERROR: (friendsForUsername:password:) Server returned failure. Code: %@, description: %@", 
			  [[response object] objectForKey:@"faultCode"], 
			  [[response object] objectForKey:@"faultString"]);
		return nil;
	} else {
		return [response object];
	}
}

- (LJResponseDictionary *)tagsForUsername:(NSString *)anUsername journal:(NSString *)aJournal password:(NSString *)aPassword {
	XMLRPCRequest *request = [[XMLRPCRequest alloc] initWithURL: serverURL];
	
	NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:
								anUsername,	@"username",
								aPassword, 	@"password",
								aJournal, 	@"usejournal",
								@"clear", 	@"auth_method",
								@"1",		@"ver",
								nil];
	
	[request setMethod: @"LJ.XMLRPC.getusertags" withParameter: parameters];
	
	XMLRPCResponse *response = [XMLRPCConnection sendSynchronousXMLRPCRequest:request];	
    [request release];
	
	if (!response) {
		[[NSNotificationCenter defaultCenter] postNotificationName:LJServerResponceErrorNotification object:self];
		NSLog(@"ERROR: (tagsForUsername:journal:password:) Failed to get responce from server");
		return nil;
	}	
	if ([response isFault]) {
		NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
							  [[response object] objectForKey:@"faultCode"], @"LJServerErrorCode",
							  [[response object] objectForKey:@"faultString"], @"LJServerErrorString",
							  nil];
		[[NSNotificationCenter defaultCenter] postNotificationName:LJServerLivejournalErrorNotification object:self userInfo:info];
		NSLog(@"ERROR: (tagsForUsername:journal:password:) Server returned failure. Code: %@, description: %@", 
			  [[response object] objectForKey:@"faultCode"], 
			  [[response object] objectForKey:@"faultString"]);
		return nil;
	} else {
		return [response object];
	}
}

- (int)postEntry:(LJEntry *)anEntry  password:(NSString *)aPassword{
	LJJournal* journal = [anEntry journal];
	LJAccount* account = [journal account];
	
	XMLRPCRequest *request = [[XMLRPCRequest alloc] initWithURL: serverURL];

	// Set request general parameters
	NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithObjectsAndKeys:
									   [account username],	@"username",
									   aPassword, 	@"password",
									   @"clear", 	@"auth_method",
									   [NSNumber numberWithInt:1], @"ver",
									   nil];

	// If enry has an ID it was already posted
	if ([anEntry isPosted]) {
		[request setMethod: @"LJ.XMLRPC.editevent" withParameter: parameters];
		[parameters setObject:[NSNumber numberWithInt:[anEntry entryID]] forKey:@"itemid"];
	} else {
		[request setMethod: @"LJ.XMLRPC.postevent" withParameter: parameters];
	}	

	// Add entry data to request parameters
	if (![journal isDefault]) {
		[parameters setObject:[journal name] forKey:@"usejournal"];
	}
	
	[parameters setObject:@"mac" forKey:@"lineendings"];
	
	[parameters setObject:[LJTypograph stringWithTypoFrom:[anEntry content]] forKey:@"event"];
	[parameters setObject:[LJTypograph stringWithTypoFrom:[anEntry subject]] forKey:@"subject"];
	
	switch ([anEntry privacyLevel]) {
		case LJPublicEntry:
			[parameters setObject:@"public" forKey:@"security"];
			break;
		case LJFriendsOnlyEntry:
			[parameters setObject:@"usemask" forKey:@"security"];
			[parameters setObject:@"1" forKey:@"allowmask"];
			break;
		case LJPrivateEntry:
			[parameters setObject:@"private" forKey:@"security"];
			break;
		case LJGroupAllowedEntry:
			[parameters setObject:@"usemask" forKey:@"security"];
			UInt32 allowmask = 0;
			for(LJFriendsGroup* group in [anEntry allowedGroups]) {
				allowmask |= (1 << [group  groupID]);
			}
			[parameters setObject:[NSNumber numberWithInt:allowmask] forKey:@"allowmask"];
			break;
		default:
			break;
	}
	
	NSDate* entryDate;
	if ([anEntry isBackdated]) {
		entryDate = [anEntry backdatedDate];
	} else {
		entryDate = [NSDate date];
	}	
	[parameters setObject:[entryDate descriptionWithCalendarFormat:@"%Y" timeZone:nil locale:nil] forKey:@"year"];
	[parameters setObject:[entryDate descriptionWithCalendarFormat:@"%m" timeZone:nil locale:nil] forKey:@"mon"];
	[parameters setObject:[entryDate descriptionWithCalendarFormat:@"%d" timeZone:nil locale:nil] forKey:@"day"];
	[parameters setObject:[entryDate descriptionWithCalendarFormat:@"%H" timeZone:nil locale:nil] forKey:@"hour"];
	[parameters setObject:[entryDate descriptionWithCalendarFormat:@"%M" timeZone:nil locale:nil] forKey:@"min"];
	
	NSMutableDictionary* metaInfo = [NSMutableDictionary dictionary];
	[metaInfo setObject:[anEntry location] forKey:@"current_location"];
	[metaInfo setObject:[anEntry currentMood] forKey:@"current_mood"];
	[metaInfo setObject:[anEntry currentMusic] forKey:@"current_music"];

	if ([anEntry isBackdated]) {
		[metaInfo setObject:@"1" forKey:@"opt_backdated"];
	}
	
	switch ([anEntry commentsAllowed]) {
		case LJAllowComments:
			[metaInfo setObject:@"0" forKey:@"opt_nocomments"];
			break;
		case LJDenyComments:
			[metaInfo setObject:@"1" forKey:@"opt_nocomments"];
			break;
		default:
			break;
	}
	
	switch ([anEntry notificationsAllowed]) {
		case LJAllowNotifications:
			[metaInfo setObject:@"0" forKey:@"opt_noemail"];
			break;
		case LJDenyNotifications:
			[metaInfo setObject:@"1" forKey:@"opt_noemail"];
			break;
		default:
			break;
	}
	
	switch ([anEntry screenComments]) {
		case LJScreenAll:
			[metaInfo setObject:@"A" forKey:@"opt_screening"];
			break;
		case LJScreenAnonymous:
			[metaInfo setObject:@"R" forKey:@"opt_screening"];
			break;
		case LJScreenNonFriends:
			[metaInfo setObject:@"F" forKey:@"opt_screening"];
			break;
		case LJScreenNone:
			[metaInfo setObject:@"N" forKey:@"opt_screening"];
			break;
		default:
			break;
	}
	
	if (![[anEntry userpic] isEqual:[account defaultUserpic]]) {
		[metaInfo setObject:[[anEntry userpic] keyword] forKey:@"picture_keyword"];
	}
	
	NSMutableString* tags = [NSMutableString string];
	for(LJTag* tag in [anEntry tags]) {
		[tags appendFormat:@"%@, ", [tag name]];
	}
	[metaInfo setObject:tags forKey:@"taglist"];
	
	[metaInfo setObject:LJServerDefaultClientVersion forKey:@"useragent"];
	
	[parameters setObject:metaInfo forKey:@"props"];	
		
	XMLRPCResponse * response = [XMLRPCConnection sendSynchronousXMLRPCRequest:request];	
    [request release];
	
	if (!response) {
		[[NSNotificationCenter defaultCenter] postNotificationName:LJServerResponceErrorNotification object:self];
		NSLog(@"ERROR: Dailed to get responce from server");
		return 0;
	}
	
	if ([response isFault]) {
		NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
							  [[response object] objectForKey:@"faultCode"], @"LJServerErrorCode",
							  [[response object] objectForKey:@"faultString"], @"LJServerErrorString",
							  nil];
		[[NSNotificationCenter defaultCenter] postNotificationName:LJServerLivejournalErrorNotification object:self userInfo:info];
		NSLog(@"ERROR: Server returned failure. Code: %@, description: %@", 
			  [[response object] objectForKey:@"faultCode"], 
			  [[response object] objectForKey:@"faultString"]);
		return 0;
	} else {
		return [[[response object] objectForKey:@"itemid"] intValue];
	}

}

@end
